"""This module provides is a highly configurable ListEditor
"""
from etgui import *
import CmdNo
from PageLayout import gTabSheetStyle,gScrollerSize,ShowSubDialog,PotTextField

##############################################################

class AbstrListEditor:
	"""Implements all the logic of a ListEditor. 
	To enable the editing features, subclasses have to override some methods.
	"""
	# Methods that start with a single _ are supposed to be private 
	# (but we are less strict)
	def __init__(self):
		self.__sel_changed=[]
		self.__selected=-1
		self.__collview=None
		self.__collection=OrdCollection()
		cv=CollectionView(self._Control)
		self._SetCollView(cv)
	#  W H A T   T O   O V E R R I D E ?
	#-----------------------------------
	# Here is a list of method names that you may override. All methods
	# (except *Swap) take one integer argument which it the position of the
	# item to edit. The *Swap take two arguments: the position of the to items
	# that are swaped.
	# If a Do* method for a feature is not defined, the feature will not
	# be available via the buttons.
	DoInsert =DoNew =DoEdit =DoRename =DoCopy =DoDelete =DoSwap =None
	DoNext   =DoPrev=None
	#-------------------------------------------
	def CanChangeSelection(self,i):
		return 1 # finish currenttly selected item 
	# If a Can* method returns 0, the associated button is disabled.
	def CanNew(self,i):
		return 1
	def CanEdit(self,i):
		return 1
	def CanRename(self,i):
		return 1
	def CanCopy(self,i):
		return 1
	def CanDelete(self,i):
		return 1
	def CanSwap(self,i,j):
		return 1
	def CanNext(self,i):
		return 1
	def CanPrev(self,i):
		return 1
	# Some methods that may be overriden
	def DoSetItems(self,items):
		# Called when the items are set
		return items
	def DoGetItems(self):
		# Called when the items are get
		return []
	def DoSelect(self,i):
		# Called when the selection changes
		pass
	def DoGetButtonConstructors(self):
		"""This method returns a list of action button constructors"""
		standard= [
			self.MakeEditButton,
			self.MakeNewButton,
			self.MakeCopyButton,
			self.MakeRenameButton,
			self.MakeDeleteButton
		]
		new =self.GetActionButtonConstructors()
		move= [
			self.MakeUpButton,
			self.MakeDownButton,
			self.MakePrevButton,
			self.MakeNextButton,
		]
		return standard+new+move
	def GetActionButtonConstructors(self):
		return []
	#--- PUBLIC METHODS --------------------------
	def SetItems(self,items):
		items=self.DoSetItems(items)
		self.__collection=OrdCollection()
		for v in items:
			item=self.DoMakeItem(v)
			self.__collection.Add(item)
		if self.__collview:
			self.__collview.SetCollection(self.__collection)
			self.Select(max(self.__selected,0))
	def GetItems(self):
		return self.DoGetItems()
	def Select(self,i):
		if not self.CanChangeSelection(self.__selected):
			return
		if self.__collview:
			if i>=self._Size():
				i=self._Size()-1
			self.__collview.SelectItem(i)
			self._SelectionChanged(i)
		else:
			self._SelectionChanged(-1)
	# All action button constructors
	label_new="New..."
	def MakeNewButton(self):
		if self.DoInsert and self.DoNew:
			return self.NewButton(self.label_new,self._New,self.CanNew,1)
	label_edit="Advanced..."
	if gTabSheetStyle:
		label_edit="Properties..."
	def MakeEditButton(self):
		if self.DoEdit:
			return self.NewButton(self.label_edit,self._Edit,self.CanEdit)
	label_rename="Rename..."
	def MakeRenameButton(self):
		if self.DoRename:
			return self.NewButton(self.label_rename,self._Rename,self.CanRename)
	label_copy="Copy..."
	def MakeCopyButton(self):
		if self.DoInsert and self.DoCopy:
			return self.NewButton(self.label_copy,self._Copy,self.CanCopy)
	label_dele="Delete"
	def MakeDeleteButton(self):
		if self.DoDelete:
			return self.NewButton(self.label_dele,self._Delete,self.CanDelete)
	label_up="Up"
	def MakeUpButton(self):
		if self.DoSwap:
			return self.NewButton(self.label_up,self._Up,self._CanUp)
	label_down="Down"
	def MakeDownButton(self):
		if self.DoSwap:
			return self.NewButton(self.label_down,self._Down,self._CanDown)
	label_next="Next"
	def MakeNextButton(self):
		if self.DoNext:
			return self.NewButton(self.label_next,self._Next,self._CanNext)
	label_prev="Prev"
	def MakePrevButton(self):
		if self.DoPrev:
			return self.NewButton(self.label_prev,self._Prev,self._CanPrev)
	# The general constructor
	def NewButton(self,label,action,enabler=None,without_selection=0):
		b=ActionButton(label,action=action)
		if enabler:
			self.__sel_changed.append((b,enabler,without_selection))
		return b
	def GetSelected(self):
		return self.__selected
	#--PROTECTED METHODS-----------------------------------------
	# IMPLEMENTATION DETAILS:
	def _GetCollView(self):
		return self.__collview
	def MakeCollView(self,scrollersize=None):
		if scrollersize is None:
			scrollersize=Point(130,180)
		return Scroller(self._GetCollView(),scrollersize)
	def MakeButtons(self):
		"""Returns the avaibalbe buttons"""
		buttons=[]
		for f in self.DoGetButtonConstructors():
			b=f()
			if b is not None:
				buttons.append(b)
		self._SelectionChanged(self.__selected)
		return buttons
	def _SetCollView(self,collview):
		self.__collview=collview
		self.__collview.SetCollection(self.__collection)
		self.Select(0)
	def _ObserveSelectionChange(self,i):
		if i==self.__selected:
			return
		if not self.CanChangeSelection(self.__selected):
			self.__collview.SelectItem(self.__selected) # undo the selection change
		else:
			self._SelectionChanged(i)
 
	def _Control(self,id,part,v):
		if part==CmdNo.cPartCollSelect:
			self._ObserveSelectionChange(v)
		elif part==CmdNo.cPartCollDoubleSelect:
			self._ObserveSelectionChange(v)
			if self.DoEdit and self.CanEdit(self.__selected):
				self._Edit()
	def _Size(self):
		return self.__collection.Size()
	# ---------------------------------------
	def _CanUp(self,i):
		return self._CanSwap(i-1,i)
	def _CanDown(self,i):
		return self._CanSwap(i,i+1)
	def _CanSwap(self,i,j):
		if max(i,j)>=self._Size() or min(i,j)<0:
			return 0
		if self.CanSwap:
			return self.CanSwap(i,j)
		return 1
	def _CanPrev(self,i):
		return i >= 1 and self.CanPrev(i)
	def _CanNext(self,i):
		return 0 <= i < self._Size()-1 and self.CanNext(i)
	#-------------------------------------------
	def _SelectionChanged(self,i):
		oldsel=self.__selected
		self.__selected=i
		self.UpdateButtons()
		if oldsel!=-1 or oldsel!=self.__selected:
			self.DoSelect(self.__selected)
	def UpdateButtons(self):
		i=self.__selected
		has_selection=i>=0 and i<self._Size()
		for b,enabler,without_selection in self.__sel_changed:
			enable=(has_selection or without_selection) and enabler(i)
			b.Enable(enable,1)
	def _New(self):
		if self.__selected<0:
			i=self._Size()
		else:
			i=self.__selected
		v=self.DoNew(i)
		if v is not None:
			self._Insert(i,v)
	def Replace(self,v):
		if v is not None:
			self._Delete()
			self._Insert(self.__selected,v)
	def Insert(self,v):
		self._Insert(0,v)
	def _Insert(self,i,v):
		if v is not None:
			newpos=self.DoInsert(i,v)
			if newpos is not None:
				i=newpos
				item=self.DoMakeItem(v)
				self.__collection.AddAt(item,i) 
				self.Select(i)
	def Delete(self,i):
		self.Select(i)
		self._Delete()
	def _Delete(self):
		if self.CanDelete(self.__selected):
			ok=self.DoDelete(self.__selected)
			if ok is None:
				raise RuntimeError,"DoDelete returned None!"
			if ok:
				old=self.__collection.RemoveAt(self.__selected)
				AddCleanupObject(old)
				self.Select(self.__selected)
	def _Edit(self):
		self.DoEdit(self.__selected)
	def _Copy(self):
		if self.__selected<0:
			i=self._Size()
		else:
			i=self.__selected
		v=self.DoCopy(i)
		if v:
			self._Insert(i,v)
	def _Rename(self):
		v=self.DoRename(self.__selected)
		if v is not None:
			self._Delete()
			self._Insert((max(self.__selected,0)),v)
	def _Swap(self,i,j):
		sel=self.__selected
		a=self.__collection.At(i)
		b=self.__collection.At(j)
		self.__collection.PutAt(b,i)
		self.__collection.PutAt(a,j)
		self.DoSwap(i,j)
		if sel==i:
			self.Select(j)
		elif sel==j:
			self.Select(i)
	def _Up(self):
		self._Swap(self.__selected,self.__selected-1)
	def _Down(self):
		self._Swap(self.__selected,self.__selected+1)
	def _Next(self):
		self.Select(self.__selected+1)
	def _Prev(self):
		self.Select(self.__selected-1)

##############################################################
class ListEditor(AbstrListEditor):
	"""A class that does simple list editing"""
	def __init__(self):
		self.items=[]
		AbstrListEditor.__init__(self)
	def ListChanged(self):
		pass
	def DoSetItems(self,items):
		self.items=items
		return items
	def DoGetItems(self):
		return self.items
	def DoMakeItem(self,v):
		return TextItem(str(v))
	def DoInsert(self,i,v):
		self.items.insert(i,v)
		self.ListChanged()
		return i
	def DoDelete(self,i):
		del self.items[i]
		self.ListChanged()
		return 1
	def DoSwap(self,i,j):
		l=self.items
		l[i],l[j]=l[j],l[i]
		self.ListChanged()

class SortedListEditorMixin:
	# Inherit this class before ListEditor and your 
	# new listviews will always be sorted :-)
	"""A class that does simple list editing"""
 
	DoSwap=None # disable swaping (The Up/Down buttons)
 
	def DoSetItems(self,items):
		self.items=list(items)
		self.items.sort()
		return self.items
	def DoInsert(self,i,v):
		k=-1
		for k in range(len(self.items)):
			if self.items[k]>v:
				break
		else:       # Have you ever seen the else construct in a for loop?
			k=k+1   # It is called when we do not break the loop.
		self.items.insert(k,v)
		self.ListChanged()
		return k

class TheListEditor(ListEditor):
	"""Shows a simple dialog to edit the item names"""
	new_item="NewItem"
	def __init__(self,what):
		self.what=what
		ListEditor.__init__(self)
	def ShowEditDialog(self,how,default):
		m=PotGuiModel()
		m.SetValue("Name",default)
		retval=ShowSubDialog("%s %s" %(how,self.what),
			m,
			PotTextField("Name")
		)
		if retval:
			return m.GetValue("Name")
		else:
			return None
	def DoNew(self,i):
		return self.ShowEditDialog("New",self.new_item)
	def DoRename(self,i):
		return self.ShowEditDialog("Rename",self.items[i])

class SimpleListEditor(TheListEditor):
	def DoCopy(self,i):
		return "Copy of %s" %self.items[i]
 
